This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Cmd+Shift+Enter.

Install packages

# install.packages("readr")
# install.packages("dplyr")
# install.packages("stringr")
# install.packages("shiny")
# install.packages("ggplot2")
# install.packages("plotly")

Load in packages

# Allows us to read-in csv files
library(readr) 
# For data manipulation
library(dplyr) 

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
# For regular expression operations 
library(stringr) 
# library(shiny)
library(ggplot2)
# Used tp create interactive visualisations
library(plotly)

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout

Load-in dataset

df <- read_csv('Data/GI_age.csv')
Rows: 42 Columns: 7── Column specification ────────────────────────────────────
Delimiter: ","
chr (4): England and Wales Code, England and Wales, Gend...
dbl (3): Gender identity (7 categories) Code, Age (6 cat...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Brief glimpse of data structure
# But can also click on the dataset in the Environment pane
head(df, 10)
# Let's check out the dimensions

dim(df)
[1] 42  7

Data Cleaning

# str_replace_all() method finds all substrings which match the regex and replaces them with empty string
# First, let's replace any brackets with empty strings
colnames(df) <- str_replace_all(colnames(df), "\\s*\\([^)]*\\)", "")

# Lowercase column text and replace empty spaces with "_"
colnames(df) <- tolower(colnames(df))
colnames(df) <- str_replace_all(colnames(df), " ", "_")

# Let's see if it worked..
head(df)

Pipes and other operators..

So, we’ve already come across the assignment operator ‘<-’ which is used to assign a value. E.g. df <- read_csv(‘Data/GI_age.csv’), here we assign our csv file to a dataframe variable called ‘df’.

But, we’re now going to encounter the pipe operator ‘%>%’ which can seem intimidating at first but is actually pretty simple. It’s used to pass the result of one function directly into the next function. E.g. df <- df %>% filter(gender_identity_code != -8), here we start with our df and pass it to the filter function using the pipe operator. This basically supplies the filter() function with its first argument, which is the dataframe to filter on. And here we encounter a logical operator ‘!=’ within the filter() function, which specifies that we should only keep rows where gender_identity_code is not equal to -8.

# Get rid of columns with 0 observations
df <- df %>% 
  filter(gender_identity_code != -8) 

# Check it worked

head(df, 10)
# Get rid of redundant age category
# Further filter data
df <- df %>%
  filter(age_code != 1)
# Clean up the values in the 'age' column. Let's shorten them.

# Chain str_replace() calls together to apply multiple string replacements in succession
# Each str_replace() call is applied to the result of the previous one
df$age <- df$age %>%
  str_replace('Aged ', '') %>%
  str_replace('to', '-') %>%
  str_replace('years', '') %>%
  str_replace('and over', '+')

# We can pass our df to the select function, where we specify the column we're interested in.
# Then, we pipe the output to the head function.
df %>%
  select(age) %>%
  head()

Question

How is gender identity distributed among different age groups?

Some subquestions that this can help us answer:

Data pre-processing

Calculate percentages

Below, we use the group_by function to group the data by ‘gender_identity’ and calculate the percentage within each group. Then the mutate() function adds a new column ‘percentage’ to df, which (for each group) divides the observation by the sum of observations, multiplies it by 100, and rounds it up to 2 decimal points. We then use the ungroup function when we’re done with the grouping operation.

df <- df %>%
  group_by(gender_identity) %>%
  mutate(percentage = round((observation / sum(observation) * 100), 2)) %>%
  ungroup()

head(df)
# Directly convert to a factor with the specified order
df$gender_identity <- factor(df$gender_identity, levels = c(
  "Gender identity the same as sex registered at birth",
  "Gender identity different from sex registered at birth but no specific identity given",
  "Trans woman",
  "Trans man",
  "All other gender identities",
  "Not answered"
))
# Print the levels to ensure they are correct
print(levels(df$gender_identity))
[1] "Gender identity the same as sex registered at birth"                                  
[2] "Gender identity different from sex registered at birth but no specific identity given"
[3] "Trans woman"                                                                          
[4] "Trans man"                                                                            
[5] "All other gender identities"                                                          
[6] "Not answered"                                                                         

Interactive grouped bar chart + stacked bar chart

So, the convention when using Plotly in R, is to create our plot first by using the ggplot2 package. Then, we convert the ggplot object to a ‘plotly’ object using ‘ggplotly’. There’s a lot going on here so I’ll break some of it down. The ggplot() function initialises a ggplot object, which sets up the dataframe that will be used for the plot and specifies the aesthetic mappings which describe how variables in the data are mapped to visual properties. So, inside aes() we specify our x and y columns, and specify that we want to map our age column to fill the colour of the bars.

Meanwhile, geom_bar() is used to make bar charts, so it adds the bar geometry to the plot. And we set stat to ‘identity’, which tells ‘ggplot’ to use the value in the y-axis column (‘percentage’) for the height of the bars. By setting position to ‘dodge’ we ensure that the bars are placed next to each other.

Finally, labs() is used to add or modify labels, and theme is used to customise non-data parts of the plot like text, legend, axes. And scale_fill_discrete() controls the colour scales and here we use the name parameter to label our legend “Age”.

TLDR: we’re using the + operator and ggplot functions to build upon the base ggplot object, layering on aesthetic mappings, geometries, labels, etc.

p <- ggplot(df, aes(x = gender_identity, y = percentage, fill = age,
                    text = paste('Observation:', observation))) +  # Include observation info
  geom_bar(stat = "identity", position = "dodge") +
  labs(title = 'Distribution of Gender Identity Categories Among Age Groups',
       x = 'Gender Identity', y = 'Percentage') +
  theme(plot.title = element_text(hjust = 0.5)) +
  scale_fill_discrete(name = "Age")

# Let's take a look at our static graph
p

Hmm okay. Not too shabby, but we’re definitely going to have to do something about our x-axis labels, as right now everything is pretty cluttered. Maybe we could rotate them, or just rename them. We’ll get round to it. But for now, let’s make this thing interactive.

# Convert ggplot object to a plotly object for interactivity
fig <- ggplotly(p, tooltip = c("y", "fill", "text"), width = 700, height = 500)  # Specify tooltip components


# Let's check it out
fig
NA
tickvals <- 1:length(levels(df$gender_identity))

# Custom tick labels corresponding to the levels
ticktext <- c(
  "Cisgender", 
  "Gender identity different from sex",
  "Trans woman",
  "Trans man",
  "All other identities",
  "Not answered"
)

Tooltips

When using different R libraries geared towards interactive visualisations, you’ll often come across ‘tooltips’. These are small boxes that provide information when a user hovers over a part of a data visualisation such as: a point on a graph, a bar in a bar chart, or a segment in a pie chart. They are used to display additional information about the data point or object, providing more context without cluttering up the chart.

# Specify custom tick labels with the corresponding tick values
fig <- fig %>%
  layout(
    title = list(text = 'Distribution of Gender Identity Categories Among Age Groups', x = 0.5),
    xaxis = list(
      title = list('Gender Identity',
      standoff = 15),
      tickmode = "array",
      tickvals = tickvals,
      ticktext = ticktext,
      tickangle = -45
    ),
    yaxis = list(title = list('Percentage',
                 standoff = 25)))

fig

Dataset 2

# Load in dataset
df2 <- read_csv('Data/GI_ethnic.csv')
Rows: 10592 Columns: 7── Column specification ────────────────────────────────────
Delimiter: ","
chr (4): Lower tier local authorities Code, Lower tier l...
dbl (3): Gender identity (4 categories) Code, Ethnic gro...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Brief glimpse at underlying data structure
head(df2, 10)
# Remove all text within parentheses from column names and replace it with an empty string

# tilde operator (~) used to apply function 'gsub' to each colname
# .x represents each colname that gsub will be applied to
df2 <- df2 %>% 
  rename_with(~ gsub("\\s*\\([^)]*\\)", "", .x))
# Lowercase all text in column names and replace spaces with underscores
df2 <- df2 %>% 
  rename_with(~ tolower(gsub(" ", "_", .x)))
# Shorten the local authority column names as they are way too long
df2 <- df2 %>% 
  rename(LA_code = lower_tier_local_authorities_code,
         LA_name = lower_tier_local_authorities)
# Let's see if it worked
colnames(df2)
[1] "LA_code"              "LA_name"             
[3] "gender_identity_code" "gender_identity"     
[5] "ethnic_group_code"    "ethnic_group"        
[7] "observation"         
# Remove 'Does not apply' categories for the gender identity and ethnic group columns
df2 <- df2 %>% 
  filter(gender_identity_code != -8, ethnic_group_code != -8)
# Display the first 30 rows
head(df2, 30)

Data pre-processing

Calculate % of each ethnic group in each LA

# First, we're going to group our data by LA_name, Ethnic group, and sum our observations
# This leaves us with the total of each ethnic group in each local authority
ethnic_totals <- df2 %>%
  group_by(LA_name, ethnic_group) %>%
  summarise(Ethnic_sum = sum(observation, na.rm = TRUE)) %>%
  ungroup()
`summarise()` has grouped output by 'LA_name'. You can override using the `.groups` argument.
# Print the first few rows to check
head(ethnic_totals)
# Calculate total observations for each local authority
la_totals <- df2 %>%
  group_by(LA_name) %>%
  summarise(LA_sum = sum(observation, na.rm = TRUE)) %>%
  ungroup()

# Print the first few rows to check
head(la_totals)
# Merge the summed ethnic group data with the total LA observations to calculate percentage of each ethnic group in each LA
merged <- merge(ethnic_totals, la_totals, by = "LA_name")
# Calculate the percentage of each ethnic group within each local authority
merged <- merged %>%
  mutate(Percentage = round((Ethnic_sum / LA_sum * 100), 2))
# Print the first few rows to check
head(merged, 10)

Calculate Non-Response Rates Within LAs

# Now calculate the non-response % for each ethnic group in each LA
# This involves grouping by LA_name, ethnic_group, and summing observations again
ethnic_group_totals <- df2 %>%
  group_by(LA_name, ethnic_group) %>%
  summarise(Ethnic_group_total = sum(observation, na.rm = TRUE)) %>%
  ungroup()
`summarise()` has grouped output by 'LA_name'. You can override using the `.groups` argument.
# This is the sum of non-responses for each ethnic group within each LA
non_response_totals <- df2 %>%
  filter(gender_identity == 'Not answered') %>%
  group_by(LA_name, ethnic_group) %>%
  summarise(Non_response_total = sum(observation, na.rm = TRUE)) %>%
  ungroup()
`summarise()` has grouped output by 'LA_name'. You can override using the `.groups` argument.
head(non_response_totals)
# Merge the totals with the non-response totals
merged_data <- merge(ethnic_group_totals, non_response_totals, by = c("LA_name", "ethnic_group"), all.x = TRUE)


head(merged_data)
# Calculate the non-response percentage for each ethnic group within each LA
merged_data <- merged_data %>%
  mutate(Eth_NR_Perc = round((Non_response_total / Ethnic_group_total * 100), 2))
head(merged_data)
# Merge the non-response data with the percentage of each ethnic group within each LA
nr <- merge(merged_data, select(merged, LA_name, ethnic_group, Percentage), by = c("LA_name", "ethnic_group"))

head(nr)

Interactive scatterplot

In this section we’re going to:

  1. Create a simple scatterplot exploring the relationship between the percentage of asian citizens within local authorities and their non-response rates

  2. Implement a widget to update our scatterplot

# Subset dataframe so we only have responses from the asian ethnic group

asian <- nr %>%
  filter(ethnic_group == 'Asian, Asian British or Asian Welsh')

head(asian)
# Initialize figure
fig <- plot_ly(data = asian,
               x = ~Percentage,
               y = ~Eth_NR_Perc,
               text = ~paste('LA Name:', LA_name, 
                             '<br>Percentage:', sprintf("%.2f", Percentage),
                             '<br>Non-response Rate:', sprintf("%.2f%%", Eth_NR_Perc),
                             '<br>Non-response Total:', Non_response_total,
                             '<br>Ethnic Group Total:', Ethnic_group_total),
               hoverinfo = "text",
               mode = 'markers',  # Specify marker points
               type = 'scatter',  # Graph type - scatterplot
               name = 'Asian')  # Default visible graph


# Customize layout 
fig <- fig %>%
  layout(title = 'Non-Response Rates of the Asian Ethnic Group Across Local Authorities',
         xaxis = list(title = 'Percentage of Ethnic Group'),
         yaxis = list(title = 'Non-response Rate'),
         width = 900,
         height = 900)
Warning: Specifying width/height in layout() is now deprecated.
Please specify in ggplotly() or plot_ly()
# Show the plot
fig
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKi4gCgojIyBJbnN0YWxsIHBhY2thZ2VzCgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJyZWFkciIpCiMgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQojIGluc3RhbGwucGFja2FnZXMoInN0cmluZ3IiKQojIGluc3RhbGwucGFja2FnZXMoInNoaW55IikKIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQpgYGAKCiMjIExvYWQgaW4gcGFja2FnZXMKCmBgYHtyfQojIEFsbG93cyB1cyB0byByZWFkLWluIGNzdiBmaWxlcwpsaWJyYXJ5KHJlYWRyKSAKIyBGb3IgZGF0YSBtYW5pcHVsYXRpb24KbGlicmFyeShkcGx5cikgCiMgRm9yIHJlZ3VsYXIgZXhwcmVzc2lvbiBvcGVyYXRpb25zIApsaWJyYXJ5KHN0cmluZ3IpIAojIGxpYnJhcnkoc2hpbnkpCmxpYnJhcnkoZ2dwbG90MikKIyBVc2VkIHRwIGNyZWF0ZSBpbnRlcmFjdGl2ZSB2aXN1YWxpc2F0aW9ucwpsaWJyYXJ5KHBsb3RseSkKYGBgCiMjIExvYWQtaW4gZGF0YXNldAoKYGBge3J9CmRmIDwtIHJlYWRfY3N2KCdEYXRhL0dJX2FnZS5jc3YnKQpgYGAKYGBge3J9CiMgQnJpZWYgZ2xpbXBzZSBvZiBkYXRhIHN0cnVjdHVyZQojIEJ1dCBjYW4gYWxzbyBjbGljayBvbiB0aGUgZGF0YXNldCBpbiB0aGUgRW52aXJvbm1lbnQgcGFuZQpgYGAKCgpgYGB7cn0KaGVhZChkZiwgMTApCmBgYAoKYGBge3J9CiMgTGV0J3MgY2hlY2sgb3V0IHRoZSBkaW1lbnNpb25zCgpkaW0oZGYpCmBgYAoKIyMgRGF0YSBDbGVhbmluZwoKYGBge3J9CiMgc3RyX3JlcGxhY2VfYWxsKCkgbWV0aG9kIGZpbmRzIGFsbCBzdWJzdHJpbmdzIHdoaWNoIG1hdGNoIHRoZSByZWdleCBhbmQgcmVwbGFjZXMgdGhlbSB3aXRoIGVtcHR5IHN0cmluZwojIEZpcnN0LCBsZXQncyByZXBsYWNlIGFueSBicmFja2V0cyB3aXRoIGVtcHR5IHN0cmluZ3MKY29sbmFtZXMoZGYpIDwtIHN0cl9yZXBsYWNlX2FsbChjb2xuYW1lcyhkZiksICJcXHMqXFwoW14pXSpcXCkiLCAiIikKCiMgTG93ZXJjYXNlIGNvbHVtbiB0ZXh0IGFuZCByZXBsYWNlIGVtcHR5IHNwYWNlcyB3aXRoICJfIgpjb2xuYW1lcyhkZikgPC0gdG9sb3dlcihjb2xuYW1lcyhkZikpCmNvbG5hbWVzKGRmKSA8LSBzdHJfcmVwbGFjZV9hbGwoY29sbmFtZXMoZGYpLCAiICIsICJfIikKCiMgTGV0J3Mgc2VlIGlmIGl0IHdvcmtlZC4uCmhlYWQoZGYpCmBgYAoKIyMjIFBpcGVzIGFuZCBvdGhlciBvcGVyYXRvcnMuLgoKU28sIHdlJ3ZlIGFscmVhZHkgY29tZSBhY3Jvc3MgdGhlIGFzc2lnbm1lbnQgb3BlcmF0b3IgJzwtJyB3aGljaCBpcyB1c2VkIHRvIGFzc2lnbiBhIHZhbHVlLiBFLmcuIGRmIDwtIHJlYWRfY3N2KCdEYXRhL0dJX2FnZS5jc3YnKSwgaGVyZSB3ZSBhc3NpZ24gb3VyIGNzdiBmaWxlIHRvIGEgZGF0YWZyYW1lIHZhcmlhYmxlIGNhbGxlZCAnZGYnLgoKQnV0LCB3ZSdyZSBub3cgZ29pbmcgdG8gZW5jb3VudGVyIHRoZSBwaXBlIG9wZXJhdG9yICclPiUnIHdoaWNoIGNhbiBzZWVtIGludGltaWRhdGluZyBhdCBmaXJzdCBidXQgaXMgYWN0dWFsbHkgcHJldHR5IHNpbXBsZS4gSXQncyB1c2VkIHRvIHBhc3MgdGhlIHJlc3VsdCBvZiBvbmUgZnVuY3Rpb24gZGlyZWN0bHkgaW50byB0aGUgbmV4dCBmdW5jdGlvbi4gRS5nLiBkZiA8LSBkZiAlPiUgZmlsdGVyKGdlbmRlcl9pZGVudGl0eV9jb2RlICE9IC04KSwgaGVyZSB3ZSBzdGFydCB3aXRoIG91ciBkZiBhbmQgcGFzcyBpdCB0byB0aGUgZmlsdGVyIGZ1bmN0aW9uIHVzaW5nIHRoZSBwaXBlIG9wZXJhdG9yLiBUaGlzIGJhc2ljYWxseSBzdXBwbGllcyB0aGUgZmlsdGVyKCkgZnVuY3Rpb24gd2l0aCBpdHMgZmlyc3QgYXJndW1lbnQsIHdoaWNoIGlzIHRoZSBkYXRhZnJhbWUgdG8gZmlsdGVyIG9uLiBBbmQgaGVyZSB3ZSBlbmNvdW50ZXIgYSBsb2dpY2FsIG9wZXJhdG9yICchPScgd2l0aGluIHRoZSBmaWx0ZXIoKSBmdW5jdGlvbiwgd2hpY2ggc3BlY2lmaWVzIHRoYXQgd2Ugc2hvdWxkIG9ubHkga2VlcCByb3dzIHdoZXJlIGdlbmRlcl9pZGVudGl0eV9jb2RlIGlzIG5vdCBlcXVhbCB0byAtOC4gCgpgYGB7cn0KIyBHZXQgcmlkIG9mIGNvbHVtbnMgd2l0aCAwIG9ic2VydmF0aW9ucwpkZiA8LSBkZiAlPiUgCiAgZmlsdGVyKGdlbmRlcl9pZGVudGl0eV9jb2RlICE9IC04KSAKCiMgQ2hlY2sgaXQgd29ya2VkCgpoZWFkKGRmLCAxMCkKYGBgCgpgYGB7cn0KIyBHZXQgcmlkIG9mIHJlZHVuZGFudCBhZ2UgY2F0ZWdvcnkKIyBGdXJ0aGVyIGZpbHRlciBkYXRhCmRmIDwtIGRmICU+JQogIGZpbHRlcihhZ2VfY29kZSAhPSAxKQoKYGBgCgpgYGB7cn0KIyBDbGVhbiB1cCB0aGUgdmFsdWVzIGluIHRoZSAnYWdlJyBjb2x1bW4uIExldCdzIHNob3J0ZW4gdGhlbS4KCiMgQ2hhaW4gc3RyX3JlcGxhY2UoKSBjYWxscyB0b2dldGhlciB0byBhcHBseSBtdWx0aXBsZSBzdHJpbmcgcmVwbGFjZW1lbnRzIGluIHN1Y2Nlc3Npb24KIyBFYWNoIHN0cl9yZXBsYWNlKCkgY2FsbCBpcyBhcHBsaWVkIHRvIHRoZSByZXN1bHQgb2YgdGhlIHByZXZpb3VzIG9uZQpkZiRhZ2UgPC0gZGYkYWdlICU+JQogIHN0cl9yZXBsYWNlKCdBZ2VkICcsICcnKSAlPiUKICBzdHJfcmVwbGFjZSgndG8nLCAnLScpICU+JQogIHN0cl9yZXBsYWNlKCd5ZWFycycsICcnKSAlPiUKICBzdHJfcmVwbGFjZSgnYW5kIG92ZXInLCAnKycpCgojIFdlIGNhbiBwYXNzIG91ciBkZiB0byB0aGUgc2VsZWN0IGZ1bmN0aW9uLCB3aGVyZSB3ZSBzcGVjaWZ5IHRoZSBjb2x1bW4gd2UncmUgaW50ZXJlc3RlZCBpbi4KIyBUaGVuLCB3ZSBwaXBlIHRoZSBvdXRwdXQgdG8gdGhlIGhlYWQgZnVuY3Rpb24uCmRmICU+JQogIHNlbGVjdChhZ2UpICU+JQogIGhlYWQoKQpgYGAKCiMjIFF1ZXN0aW9uCgpIb3cgaXMgZ2VuZGVyIGlkZW50aXR5IGRpc3RyaWJ1dGVkIGFtb25nIGRpZmZlcmVudCBhZ2UgZ3JvdXBzPwoKU29tZSBzdWJxdWVzdGlvbnMgdGhhdCB0aGlzIGNhbiBoZWxwIHVzIGFuc3dlcjoKCiogV2hhdCAlIG9mIHRyYW5zIG1lbiBhcmUgYWdlZCAxNi0yNCB5ZWFycz8KKiBBcmUgb2xkZXIgYWdlIGdyb3VwcyBvdmVycmVwcmVzZW50ZWQgaW4gdGhlICdub24tcmVzcG9uc2UnIGNhdGVnb3J5PwoKIyMgRGF0YSBwcmUtcHJvY2Vzc2luZwoKIyMjIENhbGN1bGF0ZSBwZXJjZW50YWdlcyAKCkJlbG93LCB3ZSB1c2UgdGhlIGdyb3VwX2J5IGZ1bmN0aW9uIHRvIGdyb3VwIHRoZSBkYXRhIGJ5ICdnZW5kZXJfaWRlbnRpdHknIGFuZCBjYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2Ugd2l0aGluIGVhY2ggZ3JvdXAuIFRoZW4gdGhlIG11dGF0ZSgpIGZ1bmN0aW9uIGFkZHMgYSBuZXcgY29sdW1uICdwZXJjZW50YWdlJyB0byBkZiwgd2hpY2ggKGZvciBlYWNoIGdyb3VwKSBkaXZpZGVzIHRoZSBvYnNlcnZhdGlvbiBieSB0aGUgc3VtIG9mIG9ic2VydmF0aW9ucywgbXVsdGlwbGllcyBpdCBieSAxMDAsIGFuZCByb3VuZHMgaXQgdXAgdG8gMiBkZWNpbWFsIHBvaW50cy4gV2UgdGhlbiB1c2UgdGhlIHVuZ3JvdXAgZnVuY3Rpb24gd2hlbiB3ZSdyZSBkb25lIHdpdGggdGhlIGdyb3VwaW5nIG9wZXJhdGlvbi4gCgpgYGB7cn0KZGYgPC0gZGYgJT4lCiAgZ3JvdXBfYnkoZ2VuZGVyX2lkZW50aXR5KSAlPiUKICBtdXRhdGUocGVyY2VudGFnZSA9IHJvdW5kKChvYnNlcnZhdGlvbiAvIHN1bShvYnNlcnZhdGlvbikgKiAxMDApLCAyKSkgJT4lCiAgdW5ncm91cCgpCgpoZWFkKGRmKQpgYGAKCmBgYHtyfQojIERpcmVjdGx5IGNvbnZlcnQgdG8gYSBmYWN0b3Igd2l0aCB0aGUgc3BlY2lmaWVkIG9yZGVyCmRmJGdlbmRlcl9pZGVudGl0eSA8LSBmYWN0b3IoZGYkZ2VuZGVyX2lkZW50aXR5LCBsZXZlbHMgPSBjKAogICJHZW5kZXIgaWRlbnRpdHkgdGhlIHNhbWUgYXMgc2V4IHJlZ2lzdGVyZWQgYXQgYmlydGgiLAogICJHZW5kZXIgaWRlbnRpdHkgZGlmZmVyZW50IGZyb20gc2V4IHJlZ2lzdGVyZWQgYXQgYmlydGggYnV0IG5vIHNwZWNpZmljIGlkZW50aXR5IGdpdmVuIiwKICAiVHJhbnMgd29tYW4iLAogICJUcmFucyBtYW4iLAogICJBbGwgb3RoZXIgZ2VuZGVyIGlkZW50aXRpZXMiLAogICJOb3QgYW5zd2VyZWQiCikpCmBgYAoKYGBge3J9CiMgUHJpbnQgdGhlIGxldmVscyB0byBlbnN1cmUgdGhleSBhcmUgY29ycmVjdApwcmludChsZXZlbHMoZGYkZ2VuZGVyX2lkZW50aXR5KSkKCmBgYAoKCiMjIEludGVyYWN0aXZlIGdyb3VwZWQgYmFyIGNoYXJ0ICsgc3RhY2tlZCBiYXIgY2hhcnQKClNvLCB0aGUgY29udmVudGlvbiB3aGVuIHVzaW5nIFBsb3RseSBpbiBSLCBpcyB0byBjcmVhdGUgb3VyIHBsb3QgZmlyc3QgYnkgdXNpbmcgdGhlIGdncGxvdDIgcGFja2FnZS4gVGhlbiwgd2UgY29udmVydCB0aGUgZ2dwbG90IG9iamVjdCB0byBhICdwbG90bHknIG9iamVjdCB1c2luZyAnZ2dwbG90bHknLiBUaGVyZSdzIGEgbG90IGdvaW5nIG9uIGhlcmUgc28gSSdsbCBicmVhayBzb21lIG9mIGl0IGRvd24uIFRoZSBnZ3Bsb3QoKSBmdW5jdGlvbiBpbml0aWFsaXNlcyBhIGdncGxvdCBvYmplY3QsIHdoaWNoIHNldHMgdXAgdGhlIGRhdGFmcmFtZSB0aGF0IHdpbGwgYmUgdXNlZCBmb3IgdGhlIHBsb3QgYW5kIHNwZWNpZmllcyB0aGUgYWVzdGhldGljIG1hcHBpbmdzIHdoaWNoIGRlc2NyaWJlIGhvdyB2YXJpYWJsZXMgaW4gdGhlIGRhdGEgYXJlIG1hcHBlZCB0byB2aXN1YWwgcHJvcGVydGllcy4gU28sIGluc2lkZSBhZXMoKSB3ZSBzcGVjaWZ5IG91ciB4IGFuZCB5IGNvbHVtbnMsIGFuZCBzcGVjaWZ5IHRoYXQgd2Ugd2FudCB0byBtYXAgb3VyIGFnZSBjb2x1bW4gdG8gZmlsbCB0aGUgY29sb3VyIG9mIHRoZSBiYXJzLgoKTWVhbndoaWxlLCBnZW9tX2JhcigpIGlzIHVzZWQgdG8gbWFrZSBiYXIgY2hhcnRzLCBzbyBpdCBhZGRzIHRoZSBiYXIgZ2VvbWV0cnkgdG8gdGhlIHBsb3QuIEFuZCB3ZSBzZXQgc3RhdCB0byAnaWRlbnRpdHknLCB3aGljaCB0ZWxscyAnZ2dwbG90JyB0byB1c2UgdGhlIHZhbHVlIGluIHRoZSB5LWF4aXMgY29sdW1uICgncGVyY2VudGFnZScpIGZvciB0aGUgaGVpZ2h0IG9mIHRoZSBiYXJzLiBCeSBzZXR0aW5nIHBvc2l0aW9uIHRvICdkb2RnZScgd2UgZW5zdXJlIHRoYXQgdGhlIGJhcnMgYXJlIHBsYWNlZCBuZXh0IHRvIGVhY2ggb3RoZXIuIAoKRmluYWxseSwgbGFicygpIGlzIHVzZWQgdG8gYWRkIG9yIG1vZGlmeSBsYWJlbHMsIGFuZCB0aGVtZSBpcyB1c2VkIHRvIGN1c3RvbWlzZSBub24tZGF0YSBwYXJ0cyBvZiB0aGUgcGxvdCBsaWtlIHRleHQsIGxlZ2VuZCwgYXhlcy4gQW5kIHNjYWxlX2ZpbGxfZGlzY3JldGUoKSBjb250cm9scyB0aGUgY29sb3VyIHNjYWxlcyBhbmQgaGVyZSB3ZSB1c2UgdGhlIG5hbWUgcGFyYW1ldGVyIHRvIGxhYmVsIG91ciBsZWdlbmQgIkFnZSIuIAoKVExEUjogd2UncmUgdXNpbmcgdGhlICsgb3BlcmF0b3IgYW5kIGdncGxvdCBmdW5jdGlvbnMgdG8gYnVpbGQgdXBvbiB0aGUgYmFzZSBnZ3Bsb3Qgb2JqZWN0LCBsYXllcmluZyBvbiBhZXN0aGV0aWMgbWFwcGluZ3MsIGdlb21ldHJpZXMsIGxhYmVscywgZXRjLgoKYGBge3J9CnAgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IGdlbmRlcl9pZGVudGl0eSwgeSA9IHBlcmNlbnRhZ2UsIGZpbGwgPSBhZ2UsCiAgICAgICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlKCdPYnNlcnZhdGlvbjonLCBvYnNlcnZhdGlvbikpKSArICAjIEluY2x1ZGUgb2JzZXJ2YXRpb24gaW5mbwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBsYWJzKHRpdGxlID0gJ0Rpc3RyaWJ1dGlvbiBvZiBHZW5kZXIgSWRlbnRpdHkgQ2F0ZWdvcmllcyBBbW9uZyBBZ2UgR3JvdXBzJywKICAgICAgIHggPSAnR2VuZGVyIElkZW50aXR5JywgeSA9ICdQZXJjZW50YWdlJykgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIkFnZSIpCgojIExldCdzIHRha2UgYSBsb29rIGF0IG91ciBzdGF0aWMgZ3JhcGgKcApgYGAKCkhtbSBva2F5LiBOb3QgdG9vIHNoYWJieSwgYnV0IHdlJ3JlIGRlZmluaXRlbHkgZ29pbmcgdG8gaGF2ZSB0byBkbyBzb21ldGhpbmcgYWJvdXQgb3VyIHgtYXhpcyBsYWJlbHMsIGFzIHJpZ2h0IG5vdyBldmVyeXRoaW5nIGlzIHByZXR0eSBjbHV0dGVyZWQuIE1heWJlIHdlIGNvdWxkIHJvdGF0ZSB0aGVtLCBvciBqdXN0IHJlbmFtZSB0aGVtLiBXZSdsbCBnZXQgcm91bmQgdG8gaXQuIEJ1dCBmb3Igbm93LCBsZXQncyBtYWtlIHRoaXMgdGhpbmcgaW50ZXJhY3RpdmUuCgpgYGB7cn0KIyBDb252ZXJ0IGdncGxvdCBvYmplY3QgdG8gYSBwbG90bHkgb2JqZWN0IGZvciBpbnRlcmFjdGl2aXR5CmZpZyA8LSBnZ3Bsb3RseShwLCB0b29sdGlwID0gYygieSIsICJmaWxsIiwgInRleHQiKSwgd2lkdGggPSA3MDAsIGhlaWdodCA9IDUwMCkgICMgU3BlY2lmeSB0b29sdGlwIGNvbXBvbmVudHMKCgojIExldCdzIGNoZWNrIGl0IG91dApmaWcKCmBgYAoKYGBge3J9CnRpY2t2YWxzIDwtIDE6bGVuZ3RoKGxldmVscyhkZiRnZW5kZXJfaWRlbnRpdHkpKQoKIyBDdXN0b20gdGljayBsYWJlbHMgY29ycmVzcG9uZGluZyB0byB0aGUgbGV2ZWxzCnRpY2t0ZXh0IDwtIGMoCiAgIkNpc2dlbmRlciIsIAogICJHZW5kZXIgaWRlbnRpdHkgZGlmZmVyZW50IGZyb20gc2V4IiwKICAiVHJhbnMgd29tYW4iLAogICJUcmFucyBtYW4iLAogICJBbGwgb3RoZXIgaWRlbnRpdGllcyIsCiAgIk5vdCBhbnN3ZXJlZCIKKQoKYGBgCgojIyBUb29sdGlwcyAKCldoZW4gdXNpbmcgZGlmZmVyZW50IFIgbGlicmFyaWVzIGdlYXJlZCB0b3dhcmRzIGludGVyYWN0aXZlIHZpc3VhbGlzYXRpb25zLCB5b3UnbGwgb2Z0ZW4gY29tZSBhY3Jvc3MgJ3Rvb2x0aXBzJy4gVGhlc2UgYXJlIHNtYWxsIGJveGVzIHRoYXQgcHJvdmlkZSBpbmZvcm1hdGlvbiB3aGVuIGEgdXNlciBob3ZlcnMgb3ZlciBhIHBhcnQgb2YgYSBkYXRhIHZpc3VhbGlzYXRpb24gc3VjaCBhczogYSBwb2ludCBvbiBhIGdyYXBoLCBhIGJhciBpbiBhIGJhciBjaGFydCwgb3IgYSBzZWdtZW50IGluIGEgcGllIGNoYXJ0LiBUaGV5IGFyZSB1c2VkIHRvIGRpc3BsYXkgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZGF0YSBwb2ludCBvciBvYmplY3QsIHByb3ZpZGluZyBtb3JlIGNvbnRleHQgd2l0aG91dCBjbHV0dGVyaW5nIHVwIHRoZSBjaGFydC4gCgoKYGBge3J9CiMgU3BlY2lmeSBjdXN0b20gdGljayBsYWJlbHMgd2l0aCB0aGUgY29ycmVzcG9uZGluZyB0aWNrIHZhbHVlcwpmaWcgPC0gZmlnICU+JQogIGxheW91dCgKICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gJ0Rpc3RyaWJ1dGlvbiBvZiBHZW5kZXIgSWRlbnRpdHkgQ2F0ZWdvcmllcyBBbW9uZyBBZ2UgR3JvdXBzJywgeCA9IDAuNSksCiAgICB4YXhpcyA9IGxpc3QoCiAgICAgIHRpdGxlID0gbGlzdCgnR2VuZGVyIElkZW50aXR5JywKICAgICAgc3RhbmRvZmYgPSAxNSksCiAgICAgIHRpY2ttb2RlID0gImFycmF5IiwKICAgICAgdGlja3ZhbHMgPSB0aWNrdmFscywKICAgICAgdGlja3RleHQgPSB0aWNrdGV4dCwKICAgICAgdGlja2FuZ2xlID0gLTQ1CiAgICApLAogICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gbGlzdCgnUGVyY2VudGFnZScsCiAgICAgICAgICAgICAgICAgc3RhbmRvZmYgPSAyNSkpKQoKZmlnCmBgYAoKIyMgRGF0YXNldCAyCgpgYGB7cn0KIyBMb2FkIGluIGRhdGFzZXQKZGYyIDwtIHJlYWRfY3N2KCdEYXRhL0dJX2V0aG5pYy5jc3YnKQpgYGAKYGBge3J9CiMgQnJpZWYgZ2xpbXBzZSBhdCB1bmRlcmx5aW5nIGRhdGEgc3RydWN0dXJlCmhlYWQoZGYyLCAxMCkKYGBgCgpgYGB7cn0KIyBSZW1vdmUgYWxsIHRleHQgd2l0aGluIHBhcmVudGhlc2VzIGZyb20gY29sdW1uIG5hbWVzIGFuZCByZXBsYWNlIGl0IHdpdGggYW4gZW1wdHkgc3RyaW5nCgojIHRpbGRlIG9wZXJhdG9yICh+KSB1c2VkIHRvIGFwcGx5IGZ1bmN0aW9uICdnc3ViJyB0byBlYWNoIGNvbG5hbWUKIyAueCByZXByZXNlbnRzIGVhY2ggY29sbmFtZSB0aGF0IGdzdWIgd2lsbCBiZSBhcHBsaWVkIHRvCmRmMiA8LSBkZjIgJT4lIAogIHJlbmFtZV93aXRoKH4gZ3N1YigiXFxzKlxcKFteKV0qXFwpIiwgIiIsIC54KSkKYGBgCgpgYGB7cn0KIyBMb3dlcmNhc2UgYWxsIHRleHQgaW4gY29sdW1uIG5hbWVzIGFuZCByZXBsYWNlIHNwYWNlcyB3aXRoIHVuZGVyc2NvcmVzCmRmMiA8LSBkZjIgJT4lIAogIHJlbmFtZV93aXRoKH4gdG9sb3dlcihnc3ViKCIgIiwgIl8iLCAueCkpKQpgYGAKCmBgYHtyfQojIFNob3J0ZW4gdGhlIGxvY2FsIGF1dGhvcml0eSBjb2x1bW4gbmFtZXMgYXMgdGhleSBhcmUgd2F5IHRvbyBsb25nCmRmMiA8LSBkZjIgJT4lIAogIHJlbmFtZShMQV9jb2RlID0gbG93ZXJfdGllcl9sb2NhbF9hdXRob3JpdGllc19jb2RlLAogICAgICAgICBMQV9uYW1lID0gbG93ZXJfdGllcl9sb2NhbF9hdXRob3JpdGllcykKCmBgYAoKYGBge3J9CiMgTGV0J3Mgc2VlIGlmIGl0IHdvcmtlZApjb2xuYW1lcyhkZjIpCmBgYApgYGB7cn0KIyBSZW1vdmUgJ0RvZXMgbm90IGFwcGx5JyBjYXRlZ29yaWVzIGZvciB0aGUgZ2VuZGVyIGlkZW50aXR5IGFuZCBldGhuaWMgZ3JvdXAgY29sdW1ucwpkZjIgPC0gZGYyICU+JSAKICBmaWx0ZXIoZ2VuZGVyX2lkZW50aXR5X2NvZGUgIT0gLTgsIGV0aG5pY19ncm91cF9jb2RlICE9IC04KQpgYGAKCmBgYHtyfQojIERpc3BsYXkgdGhlIGZpcnN0IDMwIHJvd3MKaGVhZChkZjIsIDMwKQpgYGAKCiMjIERhdGEgcHJlLXByb2Nlc3NpbmcKCiMjIyBDYWxjdWxhdGUgJSBvZiBlYWNoIGV0aG5pYyBncm91cCBpbiBlYWNoIExBCgpgYGB7cn0KIyBGaXJzdCwgd2UncmUgZ29pbmcgdG8gZ3JvdXAgb3VyIGRhdGEgYnkgTEFfbmFtZSwgRXRobmljIGdyb3VwLCBhbmQgc3VtIG91ciBvYnNlcnZhdGlvbnMKIyBUaGlzIGxlYXZlcyB1cyB3aXRoIHRoZSB0b3RhbCBvZiBlYWNoIGV0aG5pYyBncm91cCBpbiBlYWNoIGxvY2FsIGF1dGhvcml0eQpldGhuaWNfdG90YWxzIDwtIGRmMiAlPiUKICBncm91cF9ieShMQV9uYW1lLCBldGhuaWNfZ3JvdXApICU+JQogIHN1bW1hcmlzZShFdGhuaWNfc3VtID0gc3VtKG9ic2VydmF0aW9uLCBuYS5ybSA9IFRSVUUpKSAlPiUKICB1bmdyb3VwKCkKCiMgUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIHRvIGNoZWNrCmhlYWQoZXRobmljX3RvdGFscykKYGBgCmBgYHtyfQojIENhbGN1bGF0ZSB0b3RhbCBvYnNlcnZhdGlvbnMgZm9yIGVhY2ggbG9jYWwgYXV0aG9yaXR5CmxhX3RvdGFscyA8LSBkZjIgJT4lCiAgZ3JvdXBfYnkoTEFfbmFtZSkgJT4lCiAgc3VtbWFyaXNlKExBX3N1bSA9IHN1bShvYnNlcnZhdGlvbiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpCgojIFByaW50IHRoZSBmaXJzdCBmZXcgcm93cyB0byBjaGVjawpoZWFkKGxhX3RvdGFscykKYGBgCgpgYGB7cn0KIyBNZXJnZSB0aGUgc3VtbWVkIGV0aG5pYyBncm91cCBkYXRhIHdpdGggdGhlIHRvdGFsIExBIG9ic2VydmF0aW9ucyB0byBjYWxjdWxhdGUgcGVyY2VudGFnZSBvZiBlYWNoIGV0aG5pYyBncm91cCBpbiBlYWNoIExBCm1lcmdlZCA8LSBtZXJnZShldGhuaWNfdG90YWxzLCBsYV90b3RhbHMsIGJ5ID0gIkxBX25hbWUiKQpgYGAKCmBgYHtyfQojIENhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSBvZiBlYWNoIGV0aG5pYyBncm91cCB3aXRoaW4gZWFjaCBsb2NhbCBhdXRob3JpdHkKbWVyZ2VkIDwtIG1lcmdlZCAlPiUKICBtdXRhdGUoUGVyY2VudGFnZSA9IHJvdW5kKChFdGhuaWNfc3VtIC8gTEFfc3VtICogMTAwKSwgMikpCmBgYAoKCmBgYHtyfQojIFByaW50IHRoZSBmaXJzdCBmZXcgcm93cyB0byBjaGVjawpoZWFkKG1lcmdlZCwgMTApCmBgYAoKIyMjIENhbGN1bGF0ZSBOb24tUmVzcG9uc2UgUmF0ZXMgV2l0aGluIExBcwoKYGBge3J9CiMgTm93IGNhbGN1bGF0ZSB0aGUgbm9uLXJlc3BvbnNlICUgZm9yIGVhY2ggZXRobmljIGdyb3VwIGluIGVhY2ggTEEKIyBUaGlzIGludm9sdmVzIGdyb3VwaW5nIGJ5IExBX25hbWUsIGV0aG5pY19ncm91cCwgYW5kIHN1bW1pbmcgb2JzZXJ2YXRpb25zIGFnYWluCmV0aG5pY19ncm91cF90b3RhbHMgPC0gZGYyICU+JQogIGdyb3VwX2J5KExBX25hbWUsIGV0aG5pY19ncm91cCkgJT4lCiAgc3VtbWFyaXNlKEV0aG5pY19ncm91cF90b3RhbCA9IHN1bShvYnNlcnZhdGlvbiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpCmBgYAoKYGBge3J9CiMgVGhpcyBpcyB0aGUgc3VtIG9mIG5vbi1yZXNwb25zZXMgZm9yIGVhY2ggZXRobmljIGdyb3VwIHdpdGhpbiBlYWNoIExBCm5vbl9yZXNwb25zZV90b3RhbHMgPC0gZGYyICU+JQogIGZpbHRlcihnZW5kZXJfaWRlbnRpdHkgPT0gJ05vdCBhbnN3ZXJlZCcpICU+JQogIGdyb3VwX2J5KExBX25hbWUsIGV0aG5pY19ncm91cCkgJT4lCiAgc3VtbWFyaXNlKE5vbl9yZXNwb25zZV90b3RhbCA9IHN1bShvYnNlcnZhdGlvbiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpCmBgYAoKYGBge3J9CmhlYWQobm9uX3Jlc3BvbnNlX3RvdGFscykKYGBgCmBgYHtyfQojIE1lcmdlIHRoZSB0b3RhbHMgd2l0aCB0aGUgbm9uLXJlc3BvbnNlIHRvdGFscwptZXJnZWRfZGF0YSA8LSBtZXJnZShldGhuaWNfZ3JvdXBfdG90YWxzLCBub25fcmVzcG9uc2VfdG90YWxzLCBieSA9IGMoIkxBX25hbWUiLCAiZXRobmljX2dyb3VwIiksIGFsbC54ID0gVFJVRSkKCgpoZWFkKG1lcmdlZF9kYXRhKQpgYGAKCmBgYHtyfQojIENhbGN1bGF0ZSB0aGUgbm9uLXJlc3BvbnNlIHBlcmNlbnRhZ2UgZm9yIGVhY2ggZXRobmljIGdyb3VwIHdpdGhpbiBlYWNoIExBCm1lcmdlZF9kYXRhIDwtIG1lcmdlZF9kYXRhICU+JQogIG11dGF0ZShFdGhfTlJfUGVyYyA9IHJvdW5kKChOb25fcmVzcG9uc2VfdG90YWwgLyBFdGhuaWNfZ3JvdXBfdG90YWwgKiAxMDApLCAyKSkKYGBgCgoKYGBge3J9CmhlYWQobWVyZ2VkX2RhdGEpCmBgYAoKYGBge3J9CiMgTWVyZ2UgdGhlIG5vbi1yZXNwb25zZSBkYXRhIHdpdGggdGhlIHBlcmNlbnRhZ2Ugb2YgZWFjaCBldGhuaWMgZ3JvdXAgd2l0aGluIGVhY2ggTEEKbnIgPC0gbWVyZ2UobWVyZ2VkX2RhdGEsIHNlbGVjdChtZXJnZWQsIExBX25hbWUsIGV0aG5pY19ncm91cCwgUGVyY2VudGFnZSksIGJ5ID0gYygiTEFfbmFtZSIsICJldGhuaWNfZ3JvdXAiKSkKCmhlYWQobnIpCmBgYAoKIyMgSW50ZXJhY3RpdmUgc2NhdHRlcnBsb3QKCkluIHRoaXMgc2VjdGlvbiB3ZSdyZSBnb2luZyB0bzoKCjEuIENyZWF0ZSBhIHNpbXBsZSBzY2F0dGVycGxvdCBleHBsb3JpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBwZXJjZW50YWdlIG9mIGFzaWFuIGNpdGl6ZW5zIHdpdGhpbiBsb2NhbCBhdXRob3JpdGllcyBhbmQgdGhlaXIgbm9uLXJlc3BvbnNlIHJhdGVzCgoyLiBJbXBsZW1lbnQgYSB3aWRnZXQgdG8gdXBkYXRlIG91ciBzY2F0dGVycGxvdAoKYGBge3J9CiMgU3Vic2V0IGRhdGFmcmFtZSBzbyB3ZSBvbmx5IGhhdmUgcmVzcG9uc2VzIGZyb20gdGhlIGFzaWFuIGV0aG5pYyBncm91cAoKYXNpYW4gPC0gbnIgJT4lCiAgZmlsdGVyKGV0aG5pY19ncm91cCA9PSAnQXNpYW4sIEFzaWFuIEJyaXRpc2ggb3IgQXNpYW4gV2Vsc2gnKQoKaGVhZChhc2lhbikKYGBgCgpgYGB7cn0KIyBJbml0aWFsaXplIGZpZ3VyZQpmaWcgPC0gcGxvdF9seShkYXRhID0gYXNpYW4sCiAgICAgICAgICAgICAgIHggPSB+UGVyY2VudGFnZSwKICAgICAgICAgICAgICAgeSA9IH5FdGhfTlJfUGVyYywKICAgICAgICAgICAgICAgdGV4dCA9IH5wYXN0ZSgnTEEgTmFtZTonLCBMQV9uYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPlBlcmNlbnRhZ2U6Jywgc3ByaW50ZigiJS4yZiIsIFBlcmNlbnRhZ2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+Tm9uLXJlc3BvbnNlIFJhdGU6Jywgc3ByaW50ZigiJS4yZiUlIiwgRXRoX05SX1BlcmMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+Tm9uLXJlc3BvbnNlIFRvdGFsOicsIE5vbl9yZXNwb25zZV90b3RhbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPkV0aG5pYyBHcm91cCBUb3RhbDonLCBFdGhuaWNfZ3JvdXBfdG90YWwpLAogICAgICAgICAgICAgICBob3ZlcmluZm8gPSAidGV4dCIsCiAgICAgICAgICAgICAgIG1vZGUgPSAnbWFya2VycycsICAjIFNwZWNpZnkgbWFya2VyIHBvaW50cwogICAgICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCAgIyBHcmFwaCB0eXBlIC0gc2NhdHRlcnBsb3QKICAgICAgICAgICAgICAgbmFtZSA9ICdBc2lhbicpICAjIERlZmF1bHQgdmlzaWJsZSBncmFwaAoKCiMgQ3VzdG9taXplIGxheW91dCAKZmlnIDwtIGZpZyAlPiUKICBsYXlvdXQodGl0bGUgPSAnTm9uLVJlc3BvbnNlIFJhdGVzIG9mIHRoZSBBc2lhbiBFdGhuaWMgR3JvdXAgQWNyb3NzIExvY2FsIEF1dGhvcml0aWVzJywKICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gJ1BlcmNlbnRhZ2Ugb2YgRXRobmljIEdyb3VwJyksCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdOb24tcmVzcG9uc2UgUmF0ZScpLAogICAgICAgICB3aWR0aCA9IDkwMCwKICAgICAgICAgaGVpZ2h0ID0gOTAwKQoKIyBTaG93IHRoZSBwbG90CmZpZwpgYGAKCiMjIERyb3Bkb3duIHNlbGVjdGlvbgoKV2hhdCB3ZSdyZSBnb2luZyB0byBkbyBub3csIGlzIHVzZSBQbG90bHkncyAndXBkYXRlbWVudXMnIGluIGNvbmp1bmN0aW9uIHdpdGggdGhlICd1cGRhdGUnIG1ldGhvZCB0byBjcmVhdGUgYSBkcm9wZG93biB3aGVyZSB3ZSBjYW4gc3dpdGNoIGJldHdlZW4gdGhlIEFzaWFuIGV0aG5pYyBncm91cCwgYW5kIFdoaXRlLiAKCiMjIyBTdGVwIDE6IEluaXRpYWxpc2UgZmlndXJlIGFuZCBhZGQgdHJhY2VzCgpXZSdsbCBzdGFydCBieSBjcmVhdGluZyBhIHBsb3RfbHkgZmlndXJlLiBXZSB1c2UgcGxvdGx5IGZpZ3VyZXMgaGVyZSBpbnN0ZWFkIG9mIGdncGxvdGx5LCBiZWNhdXNlIHBsb3RfbHkgb2JqZWN0cyBvZmZlciBtb3JlIGNvbnRyb2wgb3ZlciBob3cgcGxvdHMgYXJlIGNvbnN0cnVjdGVkLiBJdCBhbGxvd3MgdXMgdG8gYWRkICd0cmFjZXMnLCB3aGljaCByZWZlciB0byBhIHNldCBvZiBkYXRhLiBJbiBvdXIgZXhhbXBsZSwgd2Ugd2FudCB0byBhZGQgYSB0cmFjZSB3aXRoIHRoZSBkYXRhIHBvaW50cyByZWxhdGluZyB0byBvdXIgYXNpYW4gZXRobmljIGdyb3VwLCBhbmQgYW5vdGhlciBvbmUgZm9yIG91ciB3aGl0ZSBldGhuaWMgZ3JvdXAuIFRoaXMgd2lsbCBzdGFydCB0byBtYWtlIHNlbnNlIHdoZW4gd2UgbG9vayBhdCB0aGUgY29kZSBiZWxvdy4gCgpgYGB7cn0KIyBJbml0aWFsaXplIGEgUGxvdGx5IGZpZ3VyZQpmaWcgPC0gcGxvdF9seSgpCgpmaWcKCmBgYAoKYGBge3J9CiMgQWRkIHRyYWNlIGZvciB0aGUgQXNpYW4gZXRobmljIGdyb3VwCmZpZyA8LSBmaWcgJT4lIGFkZF90cmFjZSgKICBkYXRhID0gbnJbbnIkZXRobmljX2dyb3VwID09ICdBc2lhbiwgQXNpYW4gQnJpdGlzaCBvciBBc2lhbiBXZWxzaCcsXSwKICB4ID0gflBlcmNlbnRhZ2UsCiAgeSA9IH5FdGhfTlJfUGVyYywKICB0ZXh0ID0gfnBhc3RlKCdMQSBOYW1lOicsIExBX25hbWUpLAogIHR5cGUgPSAnc2NhdHRlcicsCiAgbW9kZSA9ICdtYXJrZXJzJywKICBuYW1lID0gJ0FzaWFuJywKICBob3ZlcmluZm8gPSAndGV4dCt4K3knLAogIHZpc2libGUgPSBUCikKCiMgQWRkIHRyYWNlIGZvciB0aGUgV2hpdGUgZXRobmljIGdyb3VwCmZpZyA8LSBmaWcgJT4lIGFkZF90cmFjZSgKICBkYXRhID0gbnJbbnIkZXRobmljX2dyb3VwID09ICdXaGl0ZTogRW5nbGlzaCwgV2Vsc2gsIFNjb3R0aXNoLCBOb3J0aGVybiBJcmlzaCBvciBCcml0aXNoJyxdLAogIHggPSB+UGVyY2VudGFnZSwKICB5ID0gfkV0aF9OUl9QZXJjLAogIHRleHQgPSB+cGFzdGUoJ0xBIE5hbWU6JywgTEFfbmFtZSksCiAgdHlwZSA9ICdzY2F0dGVyJywKICBtb2RlID0gJ21hcmtlcnMnLAogIG5hbWUgPSAnV2hpdGUnLAogIGhvdmVyaW5mbyA9ICd0ZXh0K3greScsCiAgdmlzaWJsZSA9IEYKKQoKZmlnCmBgYAoKCmBgYHtyfQoKIyBEZWZpbmUgZHJvcGRvd24gYnV0dG9ucyBmb3IgaW50ZXJhY3Rpdml0eQpmaWcgPC0gZmlnICU+JSBsYXlvdXQoCiAgdGl0bGUgPSAiTm9uLVJlc3BvbnNlIFJhdGVzIEFjcm9zcyBMb2NhbCBBdXRob3JpdGllcyIsCiAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIlBlcmNlbnRhZ2Ugb2YgRXRobmljIEdyb3VwIiksCiAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIk5vbi1yZXNwb25zZSBSYXRlIiksCiAgc2hvd2xlZ2VuZCA9IEZBTFNFLAogIHVwZGF0ZW1lbnVzID0gbGlzdCgKICAgIGxpc3QoCiAgICAgIHR5cGUgPSAiZHJvcGRvd24iLAogICAgICBidXR0b25zID0gbGlzdCgKICAgICAgICBsaXN0KAogICAgICAgICAgbWV0aG9kID0gInVwZGF0ZSIsCiAgICAgICAgICBhcmdzID0gbGlzdChsaXN0KCJ2aXNpYmxlIiA9IGxpc3QoVFJVRSwgRkFMU0UpKSwKICAgICAgICAgICAgICAgICAgICAgIGxpc3QoInRpdGxlIiA9ICJOb24tUmVzcG9uc2UgUmF0ZXMgb2YgdGhlIEFzaWFuIEV0aG5pYyBHcm91cCBBY3Jvc3MgTG9jYWwgQXV0aG9yaXRpZXMiKSksCiAgICAgICAgICBsYWJlbCA9ICJBc2lhbiIKICAgICAgICApLAogICAgICAgIGxpc3QoCiAgICAgICAgICBtZXRob2QgPSAidXBkYXRlIiwKICAgICAgICAgIGFyZ3MgPSBsaXN0KGxpc3QoInZpc2libGUiID0gbGlzdChGQUxTRSwgVFJVRSkpLAogICAgICAgICAgICAgICAgICAgICAgbGlzdCgidGl0bGUiID0gIk5vbi1SZXNwb25zZSBSYXRlcyBvZiB0aGUgV2hpdGUgRXRobmljIEdyb3VwIEFjcm9zcyBMb2NhbCBBdXRob3JpdGllcyIpKSwKICAgICAgICAgIGxhYmVsID0gIldoaXRlIgogICAgICAgICkKICAgICAgKQogICAgKQogICkKKQoKIyBEaXNwbGF5IHRoZSBmaWd1cmUKZmlnCmBgYAoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkNtZCtPcHRpb24rSSouCgpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkNtZCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLiAKClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4KCg==